package cn.slipi.admin.service.impl;

import cn.slipi.admin.common.contants.ServiceConstants;
import cn.slipi.admin.common.dto.PageDTO;
import cn.slipi.admin.common.exception.base.BizException;
import cn.slipi.admin.common.utils.*;
import cn.slipi.admin.common.utils.dozer.impl.ConvertBean;
import cn.slipi.admin.dao.TSysAdminMapper;
import cn.slipi.admin.entity.*;
import cn.slipi.admin.service.*;
import cn.slipi.admin.service.dto.in.admin.LoginDTO;
import cn.slipi.admin.service.dto.in.m.*;
import cn.slipi.admin.service.dto.out.AdminOutDTO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 系统用户表 服务实现类
 * </p>
 *
 * @author lee
 * @since 2020-10-19
 */
@Service
public class TSysAdminServiceImpl extends ServiceImpl<TSysAdminMapper, TSysAdmin> implements TSysAdminService {


    @Autowired
    private ConvertBean convertBean;

    @Autowired
    private TSysRoleService sysRoleService;

    @Autowired
    private TSysRoleAdminService sysRoleAdminService;

    @Autowired
    private TSysAdminLogService sysAdminLogService;

    @Autowired
    private TSysAdminInfoService adminInfoService;

    @Override
    public TSysAdmin getByMobile(String mobile) {
        QueryWrapper<TSysAdmin> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("mobile", mobile);
        queryWrapper.eq("status", 1);
        return this.baseMapper.selectOne(queryWrapper);
    }

    @Override
    public TSysAdmin getDetail(Integer id) {
        return baseMapper.selectById(id);
    }

    @Override
    public List<TSysAdmin> getAdminByIds(List<Integer> ids) {
        if (ids.size() <= 0) {
            return new ArrayList<>();
        }
        return baseMapper.selectBatchIds(ids);
    }

    @Override
    public TSysAdmin login(LoginDTO loginDTO) {
        TSysAdmin sysAdmin = this.getByAccountOrMobile(loginDTO.getAccount());
        if (sysAdmin == null) {
            throw new BizException(1000, "Admin.not.exist");
        }
        String salt = sysAdmin.getSalt();
        String enc = EncryptUtil.getMD5(salt + loginDTO.getPassword());
        if (sysAdmin.getPassword().equals(enc)) {
            return sysAdmin;
        } else {
            throw new BizException(1000, "login.no.error");
        }
    }

    public TSysAdmin getByAccountOrMobile(String account) {
        QueryWrapper<TSysAdmin> queryWrapper = new QueryWrapper<>();
        queryWrapper.and(wrapper -> wrapper.eq("mobile", account).or().eq("account", account));
        queryWrapper.eq("status", 1);
        return this.baseMapper.selectOne(queryWrapper);
    }

    @Override
    public IPage<TSysAdminLog> getAdminLogList(AdminLogQueryDTO dto) {
        IPage<TSysAdminLog> page = new Page<>(dto.getPageNo(), dto.getPageSize());
        QueryWrapper<TSysAdminLog> queryWrapper = new QueryWrapper<>();
        if (null != dto.getAdminId()) {
            queryWrapper.eq("ADMIN_ID", dto.getAdminId());
        }
        if (StrUtils.isNotBlank(dto.getSubject())) {
            queryWrapper.like("SUBJECT", dto.getSubject());
        }
        if (null != dto.getType() && StrUtils.isNotBlank(dto.getType().toString())) {
            queryWrapper.eq("TYPE", dto.getType());
        }
        queryWrapper.eq("STATUS", 1);
        queryWrapper.orderByDesc("UPDATED_AT");
        return this.sysAdminLogService.getBaseMapper().selectPage(page, queryWrapper);
    }

    @Override
    public Boolean initPassword(Integer id) {
        TSysAdmin admin = this.getById(id);
        if (null == admin) {
            throw new BizException(10000, "record.found.not");
        }
        String password = EncryptUtil.getMD5(admin.getSalt() + ServiceConstants.INIT_PASSWORD);
        admin.setPassword(password);
        this.baseMapper.updateById(admin);
        return true;
    }

    @Override
    @Transactional(rollbackFor = BizException.class)
    public Boolean updateAdmin(Integer adminId, AdminUpdateDTO dto) {
        TSysAdmin admin = this.getById(adminId);
        if (null == admin) {
            throw new BizException(10000, "record.found.not");
        }
        if (StringUtils.isEmpty(dto.getAccount())) {
            admin.setAccount(dto.getAccount());
        }
        if (StringUtils.isEmpty(dto.getMobile())) {
            admin.setMobile(dto.getMobile());
        }
        if (dto.getCheck() != null) {
            admin.setCheck(dto.getCheck());
        }
        if (dto.getRoleId() != null) {
            admin.setRoleId(dto.getRoleId());
        }
        if (admin.getIsAdmin().equals(1)) {
            if (admin.getCheck().equals(0)) {
                throw new BizException(10000, "super.admin.no.forb");
            }
            if (null == dto.getRoleId()) {
                admin.setRoleId(0);
            }
        }
        this.baseMapper.updateById(admin);
        changeAdminRole(adminId, dto.getRoleId());
        return true;
    }

    @Override
    @Transactional(rollbackFor = BizException.class)
    public Boolean changePassword(Integer AdminId, ChangePasswordDTO changePasswordDTO) {
        TSysAdmin admin = this.baseMapper.selectById(AdminId);
        if (null == admin) {
            throw new BizException(10000, "record.found.not");
        }
        String password = EncryptUtil.getMD5(admin.getSalt() + changePasswordDTO.getOldPassword());
        if (!password.equals(admin.getPassword())) {
            throw new BizException(10000, "password.auth.fail");
        }
        String newPassword = EncryptUtil.getMD5(admin.getSalt() + changePasswordDTO.getPassword());
        admin.setPassword(newPassword);
        Long time = DateUtil.getTimeByMinSecond();
        admin.setLastPasswordTime(time);
        this.baseMapper.updateById(admin);
        return true;
    }

    @Override
    @Transactional(rollbackFor = BizException.class)
    public Integer addAdmin(AdminCreateDTO dto) {
        TSysAdmin admin = this.getByAccount(dto.getAccount());
        if (null != admin) {
            throw new BizException(10000, "Admin.exists");
        }
        admin = new TSysAdmin();
        admin.setAccount(dto.getAccount());
        admin.setMobile(dto.getMobile());
        admin.setRoleId(dto.getRoleId());
        Long curTime = DateUtil.getTimeByMinSecond();
        admin.setLastLoginTime(curTime);
        admin.setLastPasswordTime(curTime);
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        String ip = NetUtil.getRequestIp(request);
        admin.setLastLoginIp(ip);
        String uuid = UUIDUtil.getUUID();
        admin.setSalt(uuid);
        String password = EncryptUtil.getMD5(uuid + dto.getPassword());
        admin.setPassword(password);
        admin.setCheck(dto.getCheck());
        Integer isAdmin = 0;
        admin.setIsAdmin(isAdmin);
        this.baseMapper.insert(admin);
        changeAdminRole(admin.getId(), dto.getRoleId());
        return admin.getId();
    }

    @Override
    @Transactional(rollbackFor = BizException.class)
    public PageDTO<AdminOutDTO> getAdminList(AdminQueryDTO dto) {
        IPage<TSysAdmin> page = new Page<>(dto.getPageNo(), dto.getPageSize());
        QueryWrapper<TSysAdmin> queryWrapper = new QueryWrapper<>();
        if (StrUtils.isNotBlank(dto.getAccount())) {
            queryWrapper.like("ACCOUNT", dto.getAccount());
        }
        if (StrUtils.isNotBlank(dto.getMobile())) {
            queryWrapper.like("MOBILE", dto.getMobile());
        }
        if (null != dto.getRoleId() && StrUtils.isNotBlank(dto.getRoleId().toString())) {
            queryWrapper.eq("ROLE_ID", dto.getRoleId());
        }
        if (null != dto.getCheck() && StrUtils.isNotBlank(dto.getCheck().toString())) {
            queryWrapper.eq("CHECK", dto.getCheck());
        }
        queryWrapper.orderByDesc("UPDATED_AT");
        page = this.baseMapper.selectPage(page, queryWrapper);

        List<AdminOutDTO> outDTOList = new ArrayList<>();

        List<TSysAdmin> adminList = page.getRecords();

        Map<Integer, TSysRole> roleMap = new HashMap<>();
        Map<Integer, TSysAdminInfo> adminInfoMap = new HashMap<>();
        if (adminList.size() > 0) {
            List<Integer> roleIds = adminList.stream().map(TSysAdmin::getRoleId).collect(Collectors.toList());
            QueryWrapper<TSysRole> sysRoleQueryWrapper = new QueryWrapper<>();
            sysRoleQueryWrapper.in("ID", roleIds);
            sysRoleQueryWrapper.eq("STATUS", 1);
            List<TSysRole> roleList = this.sysRoleService.getBaseMapper().selectList(sysRoleQueryWrapper);
            roleMap = roleList.stream().collect(Collectors.toMap(TSysRole::getId, role -> role));

            List<Integer> adminIds = adminList.stream().map(TSysAdmin::getId).collect(Collectors.toList());
            QueryWrapper<TSysAdminInfo> sysAdminInfoQueryWrapper = new QueryWrapper<>();
            sysAdminInfoQueryWrapper.in("Admin_ID", adminIds);
            sysAdminInfoQueryWrapper.eq("STATUS", 1);
            List<TSysAdminInfo> adminInfoList = this.adminInfoService.getBaseMapper().selectList(sysAdminInfoQueryWrapper);
            adminInfoMap = adminInfoList.stream().collect(Collectors.toMap(TSysAdminInfo::getAdminId, AdminInfo -> AdminInfo));
        }

        Map<Integer, TSysRole> finalRoleMap = roleMap;
        Map<Integer, TSysAdminInfo> finalAdminInfoMap = adminInfoMap;
        adminList.forEach(admin -> {
            AdminOutDTO adminOutDTO = new AdminOutDTO();
            this.convertBean.convert(admin, adminOutDTO);
            adminOutDTO.setRole(finalRoleMap.getOrDefault(admin.getRoleId(), null));
            adminOutDTO.setAdminInfo(finalAdminInfoMap.getOrDefault(admin.getId(), null));
            outDTOList.add(adminOutDTO);
        });
        return PageDTO.of(page, outDTOList);
    }

    @Override
    @Transactional(rollbackFor = BizException.class)
    public AdminDTO getDetail(Long id) {
        TSysAdmin admin = this.baseMapper.selectById(id);
        if (null == admin) {
            throw new BizException(10000, "record.found.not");
        }
        return this.convertBean.convert(admin, AdminDTO.class);
    }

    @Override
    @Transactional(rollbackFor = BizException.class)
    public TSysAdmin checkAdmin(CheckAdminDTO checkAdminDTO) {
        TSysAdmin admin = this.getByAccount(checkAdminDTO.getAccount());
        if (null != admin) {
            String password = checkAdminDTO.getPassword();
            String salt = admin.getSalt();
            String enc = EncryptUtil.getMD5(salt + password);
            if (admin.getPassword().equals(enc)) {
                return admin;
            }
        }
        return null;
    }

    @Override
    @Transactional(rollbackFor = BizException.class)
    public Boolean updateLoginInfo(TSysAdmin admin, LoginInfoDTO loginInfoDTO) {
        if (null != admin) {
            admin.setLastLoginTime(loginInfoDTO.getLastLoginTime());
            HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
            admin.setLastLoginIp(NetUtil.getRequestIp(request));
            this.baseMapper.updateById(admin);
            return true;
        }
        return false;
    }

    @Transactional(rollbackFor = BizException.class)
    public TSysAdmin getByAccount(String account) {
        QueryWrapper<TSysAdmin> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("ACCOUNT", account);
        queryWrapper.eq("STATUS", 1);
        return this.baseMapper.selectOne(queryWrapper);
    }

    private Boolean changeAdminRole(Integer adminId, Integer roleId) {
        List<TSysRoleAdmin> roleAdminList = this.sysRoleAdminService.getRoleAdminByAdminId(adminId);
        //需删除的对应关系
        List<TSysRoleAdmin> delRoleAdminList = roleAdminList.stream()
                .filter(roleAdmin -> !roleAdmin.getRoleId().equals(roleId)).collect(Collectors.toList());
        //当前角色
        List<TSysRoleAdmin> curRoleAdmin = roleAdminList.stream()
                .filter(roleAdmin -> roleAdmin.getRoleId().equals(roleId)).collect(Collectors.toList());
        //该操作表示只支持单角色
        delRoleAdminList.forEach(delRoleAdmin -> this.sysRoleAdminService.getBaseMapper().deleteById(delRoleAdmin.getId()));
        if (curRoleAdmin.size() == 0 && null != roleId) {
            TSysRoleAdmin roleAdmin = new TSysRoleAdmin();
            roleAdmin.setRoleId(roleId);
            roleAdmin.setAdminId(adminId);
            this.sysRoleAdminService.getBaseMapper().insert(roleAdmin);
        }
        return true;
    }
}
